package view;

import co.edu.unal.satacredito.database.constants.TransactionConstants;
import controllers.CheckSyntacticValidationProgressTask;
import controllers.TransactionResultServiceControl;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import ws.transaction.TransactionResult;

/**
 * @version 1.0 @date 13/04/2012
 * @author john
 */
public class ProgressPane extends GridPane {

    private final double minWidth = 400;
    private final double minHeight = 120;
    private final double prefPanelHeight = 80;
    private final String showText = "Mostrar Detalles";
    private final String hideText = "Ocultar Detalles";
    private final int syntactic = 1;
    private final int semantic = 2;
    private long totalLinesNumber;
    private long totalSendLinesNumber;
    private String fileID;
    private String filePath = "";
    private DoubleProperty avaliableSpace;
    private boolean sysntacticDetailsVisible = false;
    private boolean semanticDetailsVisible = false;
    private final Label fileIDL;
    private final Label fileIDL2;
    private BorderPane syntacticPane;
    private BorderPane semanticPane;
    private Label synPercentageL;
    private Label semPercentageL;
    private Label synTitleL;
    private Label semTitleL;
    private ProgressBar syntacticPB;
    private ProgressBar semanticPB;
    private TextArea synDetailsTA;
    private TextArea semDetailsTA;
    private Button synShowDetailsB;
    private Button semShowDetailsB;
    private VBox synVBox;
    private CheckSyntacticValidationProgressTask fileAnalysisTask;
    private final Application application;

    public ProgressPane(Stage primaryStage, Application application, String filePath, long totalLinesNumber) {
        this.application = application;
        this.filePath = filePath;
        this.totalLinesNumber = totalLinesNumber;
        totalSendLinesNumber = totalLinesNumber;
        setId("grid");
        prefWidthProperty().bind(primaryStage.widthProperty().subtract(16));
        prefHeightProperty().bind(primaryStage.heightProperty().subtract(38));
        setMinWidth(minWidth);
        setVgap(20);
        avaliableSpace = new SimpleDoubleProperty();
        avaliableSpace.bind(prefHeightProperty().subtract(getPadding().getTop()).subtract(getPadding().getBottom()).subtract(125));

        HBox fileIDHBox = new HBox(7);
        fileIDL = new Label("Nombre del archivo:");
        fileIDL2 = new Label(String.valueOf(filePath));
        fileIDHBox.getChildren().setAll(fileIDL, fileIDL2);

        syntacticPane = new BorderPane();
        synPercentageL = new Label("Líneas completadas / Líneas totales");
        syntacticPB = new ProgressBar(0);
        synDetailsTA = new TextArea();
        synShowDetailsB = new Button(showText);
        synTitleL = new Label("Revisión Sintáctica");
        initProgressPane(syntacticPane, synPercentageL, synTitleL, syntacticPB, synDetailsTA, synShowDetailsB, syntactic);

        semanticPane = new BorderPane();
        semPercentageL = new Label("Líneas completadas / Líneas totales");
        semanticPB = new ProgressBar(0);
        semDetailsTA = new TextArea();
        semShowDetailsB = new Button(showText);
        semTitleL = new Label("Revisión Semántica");
        initProgressPane(semanticPane, semPercentageL, semTitleL, semanticPB, semDetailsTA, semShowDetailsB, semantic);

        GridPane.setConstraints(fileIDHBox, 0, 0);
        GridPane.setConstraints(syntacticPane, 0, 1);
        GridPane.setConstraints(semanticPane, 0, 2);
        getChildren().setAll(fileIDHBox, syntacticPane, semanticPane);
    }

    /**
     * Inicializa cada nodo dentro del panel pane
     *
     * @param pane
     * @param titleL
     * @param progressBar
     * @param detailsTA
     * @param showDetailsB
     * @param validationType
     */
    private void initProgressPane(final BorderPane pane, final Label percentageL, final Label titleL, final ProgressBar progressBar, final TextArea detailsTA, final Button showDetailsB, final int validationType) {
        pane.getStyleClass().add("details-container");
        pane.setPrefHeight(prefPanelHeight);
        pane.setMinWidth(minWidth - getPadding().getLeft() - getPadding().getRight() + 36);

        percentageL.prefWidthProperty().bind(this.prefWidthProperty().subtract(this.getPadding().getLeft()).subtract(this.getPadding().getRight()));
        percentageL.setAlignment(Pos.CENTER);

        progressBar.prefWidthProperty().bind(this.prefWidthProperty().subtract(this.getPadding().getLeft()).subtract(this.getPadding().getRight()));
        progressBar.setMinWidth(minWidth - getPadding().getLeft() - getPadding().getRight() - pane.getPadding().getLeft() - pane.getPadding().getRight());

        detailsTA.prefWidthProperty().bind(this.prefWidthProperty().subtract(this.getPadding().getLeft()).subtract(this.getPadding().getRight()));
        detailsTA.setMinWidth(minWidth - getPadding().getLeft() - getPadding().getRight() - pane.getPadding().getLeft() - pane.getPadding().getRight());
        detailsTA.setMinHeight(minHeight);
        detailsTA.setEditable(false);
        detailsTA.setFocusTraversable(false);

        showDetailsB.getStyleClass().add("details-button");
        showDetailsB.prefWidthProperty().bind(this.prefWidthProperty().subtract(this.getPadding().getLeft()).subtract(this.getPadding().getRight()));
        showDetailsB.setMinWidth(minWidth - getPadding().getLeft() - getPadding().getRight() - pane.getPadding().getLeft() - pane.getPadding().getRight());
        showDetailsB.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                boolean detailsVisible = validationType == syntactic ? sysntacticDetailsVisible : semanticDetailsVisible;
                if (detailsVisible) {
                    showDetailsB.setText(showText);
                    pane.setCenter(null);
                    pane.prefHeightProperty().unbind();
                    pane.setPrefHeight(prefPanelHeight);
                } else {
                    showDetailsB.setText(hideText);
                    pane.setCenter(detailsTA);
                    pane.prefHeightProperty().bind(avaliableSpace);
                }
                if (validationType == syntactic) {
                    sysntacticDetailsVisible = !sysntacticDetailsVisible;
                } else {
                    semanticDetailsVisible = !semanticDetailsVisible;
                }
            }
        });

        synVBox = new VBox(5);
        synVBox.getStyleClass().add("top-container");
        synVBox.getChildren().setAll(titleL, percentageL, progressBar);
        pane.setTop(synVBox);
        pane.setBottom(showDetailsB);
    }

    /**
     * Inicializa el thread de consulta y relaciona su progreso con los
     * elementos graficos que informan al usuario del avance.
     *
     * @param fileAnalysisTask
     */
    protected Task createSyntacticValidationTask() {
        fileAnalysisTask = new CheckSyntacticValidationProgressTask(filePath);
        syntacticPB.progressProperty().unbind();
        syntacticPB.setProgress(0);
        syntacticPB.progressProperty().bind(fileAnalysisTask.getTheValidatorThread().progressProperty());
        fileAnalysisTask.getTheValidatorThread().progressProperty().addListener(
            new ChangeListener<Number>() {
                @Override
                public void changed(ObservableValue<? extends Number> arg0, Number oldLinesNumber, Number newLinesNumber) {
                    synPercentageL.setText(((int) (newLinesNumber.doubleValue() * totalLinesNumber)) + " / " + totalLinesNumber);
                }
            });
        fileAnalysisTask.getTheValidatorThread().messageProperty().addListener(
            new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue<? extends String> arg0, String oldMessage, String newMessage) {
                    synDetailsTA.appendText(newMessage + "\n");
                }
            });
        return fileAnalysisTask;
    }

    /**
     *
     * @return
     */
    protected Task createSemanticValidationTask() {
        Task semanticFileAnalysisTask = checkSemanticValidationProgress();
        semanticPB.progressProperty().unbind();
        semanticPB.setProgress(0);
        semanticPB.progressProperty().bind(semanticFileAnalysisTask.progressProperty());
        semanticFileAnalysisTask.progressProperty().addListener(
            new ChangeListener<Number>() {
                @Override
                public void changed(ObservableValue<? extends Number> arg0, Number oldLinesNumber, Number newLinesNumber) {
                    semPercentageL.setText(((int) (newLinesNumber.doubleValue() * totalSendLinesNumber)) + " / " + totalSendLinesNumber);
                    if (newLinesNumber.intValue() == 1) {
                        ((MainWindow) application).endFileAnalysis();
                        semDetailsTA.textProperty().unbind();
                    }
                }
            });
        semanticFileAnalysisTask.messageProperty().addListener(
            new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue<? extends String> arg0, String oldMessage, String newMessage) {
                    semDetailsTA.appendText(newMessage);
                }
            });
        return semanticFileAnalysisTask;
    }

    /**
     *
     * @return
     */
    private Task checkSemanticValidationProgress() {
        return new Task() {
            @Override
            protected Object call() throws Exception {
                System.err.println("INGRESO AL CALL");
                System.err.println("FILE ID: "+ fileID);
                System.err.println("TOTAL SEND LINES"+ totalSendLinesNumber);
                TransactionResultServiceControl resultsControl;
                try {
                    resultsControl = new TransactionResultServiceControl();
                } catch (Exception e) {
                    updateMessage("\nOcurrio un error al comenzar la validación semántica.");
                    return true;
                }
                System.err.println("BEFORE START REVISION");
                resultsControl.startRevision(fileID);
                System.err.println("AFTER START REVISION");
                String message;
                int completedLines = 0;
                while (completedLines < totalSendLinesNumber) {
                    System.err.println("INIT WHILE: COMPLETED LINES:"+completedLines);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        System.err.println("Error con la espera entre solicitudes de nuevas trazas.");
                    }
                    message = "";
                    List<TransactionResult> analysedTransactions = resultsControl.transactionResultByFile(fileID);
                    System.err.println("TAMANO LISTA TRANSACCIONES: "+analysedTransactions.size());
                    completedLines += analysedTransactions.size();
                    String resultIssue;
                    for (TransactionResult transactionResult : analysedTransactions){
                        switch(transactionResult.getResultIssue()){
                            case TransactionConstants.ISSUE_CARD:
                                resultIssue="Transacción inválida: error con la tarjeta de crédito";
                                break;
                            case TransactionConstants.ISSUE_NIN:
                                resultIssue="Transacción inválida: error con el documento de identidad";
                                break;
                            case TransactionConstants.ISSUE_NOT:
                                resultIssue="Transacción válida";
                                break;
                            default:
                                resultIssue="Estado desconocido";
                                break;
                        }
                        message += "ID "+transactionResult.getTransactionId()+": "+resultIssue+"\n";
                    }
                    updateMessage(message);
                    updateProgress(completedLines, totalSendLinesNumber);
                    if (isCancelled())
                        break;
                }
                System.err.println("TOTAL SEND LINES"+ totalSendLinesNumber);
                System.err.println("COMPLETED LINES:"+ completedLines);
                updateMessage("\nAnálisis semántico completado.");
                return true;
            }
        };
    }
    
    /**
     * Retorna el numero de lineas que son validas sintacticamente 
     * (hasta el momento)
     * @return 
     */
    public long getValidLinesNumber(){
        return fileAnalysisTask.getValidLines();
    }
    
    /**
     * 
     * @param totalSendLinesNumber 
     */
    protected void setTotalSendLinesNumber(long totalSendLinesNumber) {
        this.totalSendLinesNumber=totalSendLinesNumber;
    }

    /**
     * 
     * @param fileID 
     */
    public void setFileID(String fileID) {
        this.fileID = fileID;
    }
    
    /**
     * Retorna el ID mongo del archivo que se esta analizando
     * @return 
     */
    public String getArchiveID(){
        if(fileAnalysisTask!=null)
            return fileAnalysisTask.getArchiveID();
        return null;
    }
}